/*
 * Sensors.java
 * iRoomMonitor1.0
 * 10/26/10
 */
package RoomNanny.SpotSensors;

import com.sun.spot.sensorboard.EDemoBoard;
import com.sun.spot.sensorboard.peripheral.ISwitch;
import com.sun.spot.sensorboard.peripheral.ISwitchListener;
import com.sun.spot.io.j2me.radiogram.*;
import com.sun.spot.util.*;

import javax.microedition.io.*;
import javax.microedition.midlet.MIDlet;
import javax.microedition.midlet.MIDletStateChangeException;

/**
 * This is a midlet application which will run on a SunSPOT. The application
 * will poll its sensors, then send that data wirelessly to a SunSPOT
 * basestation listening on the same port. It also listens for any switches
 * being pressed on the spot and responds accordingly.
 *
 * @author Tyler Mathison
 */
public class Sensors extends MIDlet implements ISwitchListener {

    private static final int HOST_PORT = 67;
    private static final int SAMPLE_PERIOD = 500;  // in milliseconds

    private String hardwareAddress;
    private int id;
    private int ledMode = 0;
    private Display display = new Display();

    // Switch initialization
    private ISwitch switch1 = EDemoBoard.getInstance().getSwitches()[0];
    private ISwitch switch2 = EDemoBoard.getInstance().getSwitches()[1];

    // Light calibration information
    private int lightMin;
    private int lightMax;

    // Initial default sensor values
    private double reportedTemp = 0;
    private boolean reportedLightOn = false;
    private int reportedDoorOpening=0;

    // Radio connects to basestation
    private RadiogramConnection outConnection = null;
    private RadiogramConnection inConnection = null;

    // Incoming and outgoing packets
    private Datagram dgOut = null;
    private Datagram dgIn = null;


    /**
     * This method is called when the SunSPOT starts. It connects to the
     * basestation wirelessly, receives its assigned ID from the basestation,
     * and then polls continuously for data.
     *
     * @throws MIDletStateChangeException
     */
    protected void startApp() throws MIDletStateChangeException {


        hardwareAddress = System.getProperty("IEEE_ADDRESS"); // spot address

        // Listen for downloads/commands over USB connection
        new com.sun.spot.util.BootloaderListener().start();

        switch1.addISwitchListener(this);   // listen for input on first switch

        try {
            // Open up a connection to send data to the basestation
            outConnection = (RadiogramConnection) Connector
                    .open("radiogram://broadcast:" + HOST_PORT);
            dgOut = outConnection.newDatagram(50);
            // Open up connection to receive data sent by the basestation
            inConnection = (RadiogramConnection) Connector
                    .open("radiogram://:" + HOST_PORT);
            dgIn = inConnection.newDatagram(50);
        } catch (Exception e) {
            System.err.println("Caught " + e
                    + " in connection initialization.");
            System.exit(1);
        }

        // Get device ID
        display.sensorUnconnected(); //this means it has not yet gotten an ID
        id = getNewID();
        display.reset();

        // Initialize sensors
        Light light = new BasicLight();
        Temperature temp = new Temperature();
        DoorMotion door = new DoorState();
        Power power= new Power();

        while (true) {
            collectData(light, temp, door, power);
        }
    }
        /**
         * This method collects data from all of the sensors and sends the data
         * to the basestation if an event has occurred. It also lights the
         * LED display according to what mode it is currently in and listens
         * for a switch press to either change that mode or calibrate the
         * light sensors.
         *
         * @param light
         * @param temp
         * @param door
         * @param power
         */

        public void collectData(Light light, Temperature temp,
                DoorMotion door, Power power){

        if (switch2.isClosed())
        {
            calibrateLight(light);

        }


        double tempReading = 0;
        int lightReading = 0;
        long currentTime = 0;
        int doorOpening = 0;
        int powerPercent=0;
        try {
                currentTime = System.currentTimeMillis();
                // Get sensor values
                tempReading = temp.getCurrent();
                lightReading = light.getCurrent();
                doorOpening = door.getState();
                powerPercent = power.getPowerLevelBeta();

            } catch (Exception e) {
                System.err.println("Caught " + e +
                        " while collecting/sending sensor sample.");
            }
            boolean lightOn = lightOn(lightReading);

            // check for events
            if (stateChanged(lightOn, tempReading, doorOpening)) {
                sendData(currentTime, tempReading, lightOn,
                        doorOpening, powerPercent);
            }

            //diplay the correct LEDs
            switch (ledMode) {
                case 0:
                    if(lightOn){
                        display.lightOn();
                    }
                    else{
                        display.lightOff();
                    }

                    break;
                case 1:
                    // note: this mode does not collect data!!!
                    display.binaryID(id);
                    break;
                case 2:
                    display.powerLed(power.getLedCount());
                    break;
            }

            // Go to sleep to conserve battery
            Utils.sleep(SAMPLE_PERIOD);
    }

    /**
     * This method calibrates the light sensor.
     *
     * @param light
     */
    private void calibrateLight(Light light) {
        // Indicate that we are calibrating
        display.reset();
        display.busy();

        light.calibrate();
        lightMax = light.getMax();
        lightMin = light.getMin();

        // Indicate that we are done calibrating
        display.reset();
    }
    /**
     * Checks to see if the light is on.
     *
     * @param lightReading
     * @return
     */
    private boolean lightOn(int lightReading){
        if (lightReading > Math.abs(lightMax - lightMin)/2 + lightMin) {
            return true;
        }
        return false;
    }
    /**
     * Checks to see if any sensor readings have changed since they were last
     * sent to the basestation.
     *
     * @param lightOn
     * @param tempReading
     * @param doorOpening
     * @return
     */
    private boolean stateChanged(boolean lightOn, double tempReading, int doorOpening){
        // has light changed?
        if (lightOn != reportedLightOn) {
            return true;
        }
        // has temp changed?
        else if (Math.abs(tempReading - reportedTemp) >= 1.0) {
            return true;
        }
        // has door changed?
        else if(doorOpening != reportedDoorOpening){
            return true;
        }
        return false;
    }

        /**
         * Sends the current collected data to the basestation.
         *
         * @param currentTime
         * @param temp
         * @param lightOn
         * @param doorOpening
         * @param powerPercent
         */
        private void sendData(long currentTime, double temp, boolean lightOn,
                int doorOpening, int powerPercent) {

        if (ledMode == 0) {
            display.sendingData();
        }

        // Send our radiogram data package
        try {
            dgOut.reset();

            dgOut.writeShort(0);    // indicate that this is a data packet
            dgOut.writeInt(id);     // write ID number
            dgOut.writeLong(currentTime);
            dgOut.writeDouble(temp);
            dgOut.writeBoolean(lightOn);
            dgOut.writeInt(doorOpening);
            dgOut.writeInt(powerPercent);

            outConnection.send(dgOut);
        } catch (Exception e) {
            System.err.println("Caught exception " + e + "while sending data");
        }

        // Update last reported values
        reportedLightOn = lightOn;
        reportedTemp = temp;
        reportedDoorOpening = doorOpening;

        // Turn off LED indicator now that data has been sent
        if (ledMode == 0) {
            display.reset();
        }
    }

    /**
     * Switch callback function for when the switch button is released.
     *
     * @param sw The switch being released
     */
    public void switchReleased(ISwitch sw) {
        if (ledMode < 2) {
            ledMode = ledMode + 1;
        } else {
            ledMode = 0;
        }
        display.reset();
    }

    /**
     * Runs when a switch is pressed. Needed by Squawk VM to run sensor.
     *
     * @param sw
     */
    public void switchPressed(ISwitch sw) {

    }

    /**
     * Retrieves a ID value from the basestation. Does not return until an
     * ID is given.
     *
     * @return int the sensor ID
     */
    private int getNewID() {
        int newID = -1;

        while (newID == -1) {
            try {
                dgOut.reset();
                dgOut.writeShort(1);// indicate that this is a request for ID
                outConnection.send(dgOut);
                inConnection.receive(dgIn);
                // Only assign the id received if is the correct hardware addr
                if (dgIn.readUTF().equals(hardwareAddress)) {
                    newID = dgIn.readInt();
                    System.out.println("Got ID from basestation: " + newID);
                    inConnection.close();
                }
            } catch (Exception e) {
                System.err.println("Caught exception " + e + "while getting " +
                                   "request for ID number");
            }
        }
        return newID;
    }

    /**
     * Pauses the current application. Needed by Squawk VM to run sensor.
     */

    protected void pauseApp() {
    }
    /**
     * Destroys the current application if an unforseen exception occurs.
     *
     * @param arg0
     * @throws MIDletStateChangeException
     */

    protected void destroyApp(boolean arg0) throws MIDletStateChangeException {
        // Only called if startApp throws any exception other
        // than MIDletStateChangeException
    }
}